package com.A21.GuoSai.demo.service_entity.utils;

import com.A21.GuoSai.demo.service_entity.annotation.Function2;
import com.A21.GuoSai.demo.service_entity.entity.workflow.EndEventPojo;
import com.A21.GuoSai.demo.service_entity.entity.workflow.ExpressionFlowPojo;
import com.A21.GuoSai.demo.service_entity.entity.workflow.FlowNodePojo;
import com.A21.GuoSai.demo.service_entity.entity.workflow.WorkFlowPojo;
import io.camunda.zeebe.model.bpmn.Bpmn;
import io.camunda.zeebe.model.bpmn.BpmnModelInstance;
import io.camunda.zeebe.model.bpmn.instance.Process;
import io.camunda.zeebe.model.bpmn.instance.*;
import org.apache.commons.io.IOUtils;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

public class BpmnUtils {



    /**
     * 读取Bpmn文件，转化为一个BpmnModelInstance
     * @param bpmn
     * @return
     */
    public static BpmnModelInstance getModelInstance(String bpmn){
        InputStream bpmnStream= IOUtils.toInputStream(bpmn, StandardCharsets.UTF_8);
//        InputStream bpmnStream2=BpmnUtils.class.getClassLoader().getResourceAsStream("processes/my2.bpmn");
        return Bpmn.readModelFromStream(bpmnStream);
    }


    /**
     * 读取Bpmn文件中的BpmnProcessId和BpmnName
     * @param modelInstance
     * @return
     */
    public static WorkFlowPojo getWorkFlowMessage(BpmnModelInstance modelInstance){
        WorkFlowPojo workFlowPojo=new WorkFlowPojo();
        for(Process process:modelInstance.getModelElementsByType(Process.class)){
            workFlowPojo=new WorkFlowPojo(process.getId(),process.getName(),new Date());
            break;
        }
        workFlowPojo.setFlowDesc("默认描述:  "+workFlowPojo.getFlowName()+"--"+workFlowPojo.getFlowName());
        return workFlowPojo;
    }


    /**
     * 读取Bpmn文件中的BpmnProcessId和BpmnName，并附加工作流的描述内容
     * @param modelInstance
     * @param flowDesc
     * @return
     */
    public static WorkFlowPojo getWorkFlowMessage(BpmnModelInstance modelInstance,String flowDesc){
        WorkFlowPojo workFlowPojo=getWorkFlowMessage(modelInstance);
        workFlowPojo.setFlowDesc(flowDesc);
        return workFlowPojo;
    }


    /**
     * 获取指定组件后的exclusiveFlow结点，有的组件后面可能有连续多个Exclusive结点
     * @param modelInstance
     * @param NodeClass
     * @return
     */
    private static List<FlowNodePojo> getFlowNodes(BpmnModelInstance modelInstance, Class < ? extends FlowNode> NodeClass,
                                                   Function2<FlowNode,FlowNode,List<? extends FlowNodePojo>> funct){
        List<FlowNodePojo> expressionList=new ArrayList<FlowNodePojo>();
        boolean isStartEvent = NodeClass==StartEvent.class;

        //获取bpmnProcessId
        String bpmnProcessId = null;
        for (Process process: modelInstance.getModelElementsByType(Process.class))
            bpmnProcessId=process.getId();

        for (FlowNode event:modelInstance.getModelElementsByType(NodeClass)){
            //合并多个list
            event.getSucceedingNodes().stream().forEach(node->{ expressionList.addAll(funct.apply(node,event)); });
        }

        //这个finalBpmnProcessId存在的意义就是为了保证lambda的正常执行
        String finalBpmnProcessId = bpmnProcessId;
        expressionList.stream().forEach(
                expressionFlowPojo ->{
                    expressionFlowPojo.setFlowId(finalBpmnProcessId);
                }
        );

        return expressionList;
    }


    /**
     * 一个条件结点附带的可能有多个分支，所以要用遍历考虑
     * @param flowNode
     * @param task
     */
    private static List<ExpressionFlowPojo> getExlusiveGatewayByErgodic(FlowNode flowNode, FlowNode task){
        List<ExpressionFlowPojo> list=new ArrayList<>();

        if (flowNode.getElementType().getInstanceType()==ExclusiveGateway.class){
//            System.out.println(flowNode.getName()+"前的task为"+task.getName());
            //获取ExclusiveGateway(条件结点)的输出流
            Collection<SequenceFlow> sequenceFlows= flowNode.getOutgoing();
            //如果Exclusive的输出流不止一条，则循环遍历
            for(SequenceFlow sequenceFlow:sequenceFlows){
                ExpressionFlowPojo expressionflowPojo=new ExpressionFlowPojo();
                expressionflowPojo.setPreNodeName(task.getName());
                expressionflowPojo.setExclusiveNodeName(flowNode.getName());

                //Zeebe的Feel引擎默认考虑了一个expression包含多个表达式的情况，所以用集合存储，不过一般情况下默认只有一个表达式
                Collection<ConditionExpression> expressions=sequenceFlow.getChildElementsByType(ConditionExpression.class);
                //不过毕竟还是用了集合，所以这里使用流进行遍历，实际上这个集合只有一个元素
                expressions.stream().forEach(express -> {
                    expressionflowPojo.setExpression(express.getRawTextContent());
                });
                list.add(expressionflowPojo);
            }

            //每次遍历返回的都是一个list，所以要合并多个list
            flowNode.getSucceedingNodes().stream().forEach(
                    node->{ list.addAll(getExlusiveGatewayByErgodic(node,task)); }
            );
        }
        return list;
    }



    private static List<EndEventPojo> getEndEventByErgodic(FlowNode flowNode, FlowNode task){
        List<EndEventPojo> list=new ArrayList<>();

        if(flowNode.getElementType().getInstanceType()==ExclusiveGateway.class
                ||flowNode.getElementType().getInstanceType() == EndEvent.class){
//            System.out.println(flowNode.getName()+"前的task为"+task.getName());

            if(flowNode.getElementType().getInstanceType()==EndEvent.class){
                EndEventPojo endEventPojo=new EndEventPojo();
                endEventPojo.setEndEventNodeName(flowNode.getName());
                endEventPojo.setPreNodeName(task.getName());
                list.add(endEventPojo);
            }

            //每次遍历返回的都是一个list，所以要合并多个list
            flowNode.getSucceedingNodes().stream().forEach(
                    node->{ list.addAll(getEndEventByErgodic(node,task)); }
            );

        }

        return list;
    }



    /**
     * 获取bpmn中全部的expressionFlow
     * @param modelInstance
     * @return
     */
    public static List<ExpressionFlowPojo> getAllExpressionFlows(BpmnModelInstance modelInstance){
        List<FlowNodePojo> list1=getFlowNodes(modelInstance,StartEvent.class,BpmnUtils::getExlusiveGatewayByErgodic);
        List<FlowNodePojo> list2=getFlowNodes(modelInstance, Task.class,BpmnUtils::getExlusiveGatewayByErgodic);
        //过滤掉没有判断条件的ExclusiveFlow
        list1.addAll(list2);

        List<ExpressionFlowPojo> list=list1.stream()
                .map(flowNodePojo -> {
                    ExpressionFlowPojo expressionFlowPojo= (ExpressionFlowPojo) flowNodePojo;
                    return expressionFlowPojo;
                })
                .filter(expressionFlowPojo -> expressionFlowPojo.getExpression()!=null)
                .collect(Collectors.toList());

        list=list.stream().map(expressionFlowPojo -> {
            if(expressionFlowPojo.getExpression().startsWith("="))
                expressionFlowPojo.setExpression(expressionFlowPojo.getExpression().substring(1));
            return expressionFlowPojo;
        }).collect(Collectors.toList());
    /*System.out.println("捕获到的expressionFlowPojo: ");
        list.forEach(expressionFlowPojo -> {
            System.out.println(expressionFlowPojo.toString());
        });*/
        return list;
    }


    public static List<EndEventPojo> getAllEndEvents(BpmnModelInstance modelInstance){
        List<FlowNodePojo> list1=getFlowNodes(modelInstance,StartEvent.class,BpmnUtils::getEndEventByErgodic);
        List<FlowNodePojo> list2=getFlowNodes(modelInstance,Task.class,BpmnUtils::getEndEventByErgodic);
        list1.addAll(list2);

        List<EndEventPojo> list=list1.stream()
                .map(flowNodePojo -> {
                    EndEventPojo endEventPojo=(EndEventPojo) flowNodePojo;
                    return endEventPojo;
                }).collect(Collectors.toList());

       /* System.out.println("捕获到的endEventFlowPojo: ");
        list.forEach(expressionFlowPojo -> {
            System.out.println(expressionFlowPojo.toString());
        });*/
        return list;
    }



}
